/* ---------------------------------------------------------------------------- */
/*                  Atmel Microcontroller Software Support                      */
/*                       SAM Software Package License                           */
/* ---------------------------------------------------------------------------- */
/* Copyright (c) 2015, Atmel Corporation                                        */
/*                                                                              */
/* All rights reserved.                                                         */
/*                                                                              */
/* Redistribution and use in source and binary forms, with or without           */
/* modification, are permitted provided that the following condition is met:    */
/*                                                                              */
/* - Redistributions of source code must retain the above copyright notice,     */
/* this list of conditions and the disclaimer below.                            */
/*                                                                              */
/* Atmel's name may not be used to endorse or promote products derived from     */
/* this software without specific prior written permission.                     */
/*                                                                              */
/* DISCLAIMER:  THIS SOFTWARE IS PROVIDED BY ATMEL "AS IS" AND ANY EXPRESS OR   */
/* IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF */
/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT ARE   */
/* DISCLAIMED. IN NO EVENT SHALL ATMEL BE LIABLE FOR ANY DIRECT, INDIRECT,      */
/* INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT */
/* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA,  */
/* OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF    */
/* LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING         */
/* NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, */
/* EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.                           */
/* ---------------------------------------------------------------------------- */

/**
 * \addtogroup qspi_dma_module QSPI xDMA driver
 * \ingroup peripherals_module
 *
 *
 */

/**
 * \file
 *
 * Implementation for the SPI Flash with xDMA driver.
 *
 */


/*----------------------------------------------------------------------------
 *        Headers
 *----------------------------------------------------------------------------*/

#include "chip.h"
#ifdef __rtems__
#include "../../../utils/utility.h"
#endif /* __rtems__ */

/*----------------------------------------------------------------------------
 *        Definitions
 *----------------------------------------------------------------------------*/

/** xDMA support */

/** xDMA Link List size for SPI transmission*/
#define DMA_QSPI_LLI     2

/*-----------------------------------------------------------------------------
 *        QSPI DMA Local functions
 *----------------------------------------------------------------------------*/

/**
 * \brief SPI xDMA Rx callback
 * Invoked on SPi DMA reception done.
 * \param channel DMA channel.
 * \param pArg Pointer to callback argument - Pointer to Spid instance.
 */
static void QSPID_Spi_Cb(uint32_t channel, QspiDma_t *pArg)
{
	Qspi *pQspiHw = pArg->Qspid.pQspiHw;

	if (channel != pArg->RxChNum)
		return;

	/* Release the semaphore */
	ReleaseMutex(pArg->progress);
	QSPI_EndTransfer(pQspiHw);
	SCB_InvalidateDCache_by_Addr((uint32_t *)pArg->Qspid.qspiBuffer.pDataRx,
								 pArg->Qspid.qspiBuffer.RxDataSize);
	memory_sync();
}


/**
 * \brief QSPI xDMA Tx callback
 * Invoked on QSPi DMA Write done.
 * \param channel DMA channel.
 * \param pArg Pointer to callback argument - Pointer to Spid instance.
 */
static void QSPID_qspiTx_Cb(uint32_t channel, QspiDma_t *pArg)
{
	Qspi *pQspiHw = pArg->Qspid.pQspiHw;

	if (channel != pArg->TxChNum)
		return;

	/* Release the semaphore */
	ReleaseMutex(pArg->progress);
	QSPI_EndTransfer(pQspiHw);

	while (!QSPI_GetStatus(pArg->Qspid.pQspiHw, IsEofInst));

	memory_sync();
}


/**
 * \brief QSPI xDMA Rx callback
 * Invoked on SPi DMA reception done.
 * \param channel DMA channel.
 * \param pArg Pointer to callback argument - Pointer to Spid instance.
 */
static void QSPID_qspiRx_Cb(uint32_t channel, QspiDma_t *pArg)
{
	Qspi *pQspiHw = pArg->Qspid.pQspiHw;

	if (channel != pArg->RxChNum)
		return;

	/* Release the semaphore */
	ReleaseMutex(pArg->progress);
	QSPI_EndTransfer(pQspiHw);

	while (!QSPI_GetStatus(pArg->Qspid.pQspiHw, IsEofInst));

	SCB_InvalidateDCache_by_Addr((uint32_t *)pArg->Qspid.qspiBuffer.pDataRx,
								 pArg->Qspid.qspiBuffer.RxDataSize);
	memory_sync();
}

/**
 * \brief Configures the DMA for QSPI
 *
 * \param pQspidma  Pointer to QSPI DMA structure
 * \param Addr      Address to Read or write of QSPI flash memory
 * \param pBuffer   Pointer input/output buffer
 * \param ReadWrite Read or write memory flag
 * \returns 0 if the dma multibuffer configuration successfully; otherwise returns
 * QSPID_ERROR_XXX.
 */
static uint8_t QSPID_configureQpsiDma(QspiDma_t *pQspidma, uint32_t Addr,
									  QspiBuffer_t *pBuffer, Access_t const ReadWrite)
{
	sXdmadCfg xdmadCfg, xdmadRxCfg, xdmadTxCfg;
	uint8_t chanNum;
	uint8_t qspi_id =  pQspidma->Qspid.qspiId;
	Qspi *pQspiHw = pQspidma->Qspid.pQspiHw;
	uint32_t xdmaCndc, xdmaInt, BurstSize, ChannelWidth;


	/* Setup DMA  for QSPI */

	if (pQspidma->Qspid.qspiMode == QSPI_MR_SMM_SPI) {
		// SPI mode
		/* SPI TX DMA config */
		xdmadTxCfg.mbr_sa   =   (uint32_t)pBuffer->pDataTx;
		xdmadTxCfg.mbr_da   =   (uint32_t)&pQspiHw->QSPI_TDR;
		xdmadTxCfg.mbr_ubc  =   (pBuffer->TxDataSize);

		xdmadTxCfg.mbr_cfg =  XDMAC_CC_TYPE_PER_TRAN |
							  XDMAC_CC_MBSIZE_SINGLE |
							  XDMAC_CC_DSYNC_MEM2PER |
							  XDMAC_CC_CSIZE_CHK_1 |
							  XDMAC_CC_DWIDTH_BYTE |
							  XDMAC_CC_SIF_AHB_IF0 |
							  XDMAC_CC_DIF_AHB_IF1 |
							  XDMAC_CC_SAM_INCREMENTED_AM |
							  XDMAC_CC_DAM_FIXED_AM |
							  XDMAC_CC_PERID(XDMAIF_Get_ChannelNumber
											 (qspi_id, XDMAD_TRANSFER_TX));

		xdmadTxCfg.mbr_bc = 0;
		xdmadTxCfg.mbr_sus = 0;
		xdmadTxCfg.mbr_dus = 0;

		/* SPI RX DMA config */

		xdmadRxCfg.mbr_da  =  (uint32_t)pBuffer->pDataRx;
		xdmadRxCfg.mbr_sa  =  (uint32_t)&pQspiHw->QSPI_RDR;
		xdmadRxCfg.mbr_ubc =   (pBuffer->RxDataSize);
		xdmadRxCfg.mbr_cfg =    XDMAC_CC_TYPE_PER_TRAN |
								XDMAC_CC_MBSIZE_SINGLE |
								XDMAC_CC_DSYNC_PER2MEM |
								XDMAC_CC_CSIZE_CHK_1 |
								XDMAC_CC_DWIDTH_BYTE |
								XDMAC_CC_SIF_AHB_IF1 |
								XDMAC_CC_DIF_AHB_IF0 |
								XDMAC_CC_SAM_FIXED_AM |
								XDMAC_CC_DAM_INCREMENTED_AM |
								XDMAC_CC_PERID(XDMAIF_Get_ChannelNumber
											   (qspi_id, XDMAD_TRANSFER_RX));

		xdmadRxCfg.mbr_bc = 0;
		xdmadRxCfg.mbr_sus = 0;
		xdmadRxCfg.mbr_dus = 0;
		xdmaCndc = 0;
		/* Put all interrupts on for non LLI list setup of DMA */
		xdmaInt =  (XDMAC_CIE_BIE   |
					XDMAC_CIE_RBIE  |
					XDMAC_CIE_WBIE  |
					XDMAC_CIE_ROIE);

		memory_barrier();

		if (XDMAD_ConfigureTransfer
			(pQspidma->pXdmad, pQspidma->RxChNum, &xdmadRxCfg, xdmaCndc, 0, xdmaInt))
			return QSPID_ERROR;

		if (XDMAD_ConfigureTransfer
			(pQspidma->pXdmad, pQspidma->TxChNum, &xdmadTxCfg, xdmaCndc, 0, xdmaInt))
			return QSPID_ERROR;

		return 0;

	} else {
		if (ReadWrite == WriteAccess) {
			xdmadCfg.mbr_sa = (uint32_t)pBuffer->pDataTx;
			xdmadCfg.mbr_da = (uint32_t)(QSPIMEM_ADDR | Addr);
			xdmadCfg.mbr_ubc =  (pBuffer->TxDataSize);
			chanNum =  pQspidma->TxChNum;
			ChannelWidth = XDMAC_CC_DWIDTH_BYTE;
			BurstSize = XDMAC_CC_MBSIZE_SIXTEEN;
		} else if (ReadWrite == ReadAccess) {
			xdmadCfg.mbr_da = (uint32_t)pBuffer->pDataRx;
			xdmadCfg.mbr_sa = (uint32_t)(QSPIMEM_ADDR | Addr);
			xdmadCfg.mbr_ubc =  ((pBuffer->RxDataSize >> 2));
			chanNum =  pQspidma->RxChNum;
			ChannelWidth = XDMAC_CC_DWIDTH_WORD;
			BurstSize = XDMAC_CC_MBSIZE_SIXTEEN;
		} else {
			TRACE_ERROR(" QSPI error \n\r");
			return 1;
		}

		xdmadCfg.mbr_cfg = XDMAC_CC_TYPE_MEM_TRAN |
						   XDMAC_CC_MEMSET_NORMAL_MODE |
						   BurstSize |
						   ChannelWidth |
						   XDMAC_CC_SIF_AHB_IF1 |
						   XDMAC_CC_DIF_AHB_IF1 |
						   XDMAC_CC_SAM_INCREMENTED_AM |
						   XDMAC_CC_DAM_INCREMENTED_AM;

		xdmadCfg.mbr_bc = 0;
		xdmadCfg.mbr_sus = 0;
		xdmadCfg.mbr_dus = 0;

		xdmaCndc = 0;


		/* Put all interrupts on for non LLI list setup of DMA */
		xdmaInt =  (XDMAC_CIE_BIE   |
					XDMAC_CIE_RBIE  |
					XDMAC_CIE_WBIE  |
					XDMAC_CIE_ROIE);

		memory_barrier();

		if (XDMAD_ConfigureTransfer(pQspidma->pXdmad, chanNum, &xdmadCfg, xdmaCndc, 0,
									 xdmaInt))
			return QSPID_ERROR;

		return 0;
	}
}

/*----------------------------------------------------------------------------
 *        Exported functions
 *----------------------------------------------------------------------------*/
/**
 * \brief Initializes the pQspidma structure and the corresponding QSPI & DMA .
 * hardware select value.
 *
 * \param pQspidma  Pointer to a QspiDma_t instance.
 * \param Mode      Associated SPI peripheral.
 * \param dwConf    QSPI peripheral configuration.
 * \param pXdmad    Pointer to a Xdmad instance.
 */
uint32_t QSPID_Configure(QspiDma_t *pQspidma, QspiMode_t Mode,
						  uint32_t dwConf, sXdmad *pXdmad)
{
	/* Initialize the QSPI structure */

	QSPI_ConfigureInterface(&pQspidma->Qspid, Mode, dwConf);

	pQspidma->Qspid.qspiCommand.Instruction = 0;
	pQspidma->Qspid.qspiCommand.Option = 0;

	pQspidma->RxChNum = QSPID_CH_NOT_ENABLED;
	pQspidma->TxChNum = QSPID_CH_NOT_ENABLED;

	assert(pXdmad == &XDMAD_Instance);
	pQspidma->pXdmad = pXdmad;

	return QSPI_SUCCESS;
}



/**
 * \brief Enables a QSPI Rx channel. This function will allocate a dma Rx
 *  channel for QSPI
 *
 * \param pQspidma  Pointer to a Spid instance.

 * \returns 0 if the transfer has been started successfully; otherwise returns
 * QSPID_ERROR_LOCK is the driver is in use, or QSPID_ERROR if the command is not
 * valid.
 */
uint32_t QSPID_EnableQspiRxChannel(QspiDma_t *pQspidma)
{
	static uint16_t DmaChannel;

	/* Try to get the semaphore */
	if (pQspidma->RxChNum != QSPID_CH_NOT_ENABLED)
		return QSPID_ERROR_LOCK;

	/* Allocate a DMA channel */
	DmaChannel = XDMAD_AllocateChannel(
					 pQspidma->pXdmad, XDMAD_TRANSFER_MEMORY, XDMAD_TRANSFER_MEMORY);

	if (DmaChannel == XDMAD_ALLOC_FAILED)
		return QSPID_ERROR;

	pQspidma->RxChNum = DmaChannel;
	/* Setup callbacks*/
	XDMAD_SetCallback(pQspidma->pXdmad, pQspidma->RxChNum,
					  (XdmadTransferCallback)QSPID_qspiRx_Cb, pQspidma);

	if (XDMAD_PrepareChannel(pQspidma->pXdmad, pQspidma->RxChNum))
		return QSPID_ERROR;

	return 0;
}


/**
 * \brief Enables a QSPI Tx channel. This function will allocate a dma Tx
 * channel for QSPI
 *
 * \param pQspidma  Pointer to a Spid instance.

 * \returns 0 if the transfer has been started successfully; otherwise returns
 * QSPID_ERROR_LOCK is the driver is in use, or QSPID_ERROR if the command is
 * not valid.
 */
uint32_t QSPID_EnableQspiTxChannel(QspiDma_t *pQspidma)
{
	static uint16_t DmaChannel;

	/* Try to get the  semaphore */
	if (pQspidma->TxChNum != QSPID_CH_NOT_ENABLED)
		return QSPID_ERROR_LOCK;

	/* Allocate a DMA channel */
	DmaChannel = XDMAD_AllocateChannel(pQspidma->pXdmad,
										XDMAD_TRANSFER_MEMORY, XDMAD_TRANSFER_MEMORY);

	if (DmaChannel == XDMAD_ALLOC_FAILED)
		return QSPID_ERROR;

	pQspidma->TxChNum = DmaChannel;
	/* Setup callbacks  */
	XDMAD_SetCallback(pQspidma->pXdmad, pQspidma->TxChNum,
					  (XdmadTransferCallback)QSPID_qspiTx_Cb, pQspidma);

	if (XDMAD_PrepareChannel(pQspidma->pXdmad, pQspidma->TxChNum))
		return QSPID_ERROR;

	return 0;
}


/**
 * \brief Enables a QSPI SPI Rx channel. This function will allocate a dma
 *  Rx channel for QSPI SPI mode
 *
 * \param pQspidma  Pointer to a Spid instance.

 * \returns 0 if the transfer has been started successfully; otherwise returns
 * QSPID_ERROR_LOCK is the driver is in use, or QSPID_ERROR if the command is
 * not valid.
 */
uint32_t QSPID_EnableSpiChannel(QspiDma_t *pQspidma)
{
	static uint16_t DmaChannel;

	/* Try to get the semaphore */
	if (pQspidma->RxChNum != QSPID_CH_NOT_ENABLED)
		return QSPID_ERROR_LOCK;

	/* Try to get the  semaphore */
	if (pQspidma->TxChNum != QSPID_CH_NOT_ENABLED)
		return QSPID_ERROR_LOCK;

	/* Allocate a DMA channel */
	DmaChannel = XDMAD_AllocateChannel
				 (pQspidma->pXdmad, pQspidma->Qspid.qspiId, XDMAD_TRANSFER_MEMORY);

	if (DmaChannel == XDMAD_ALLOC_FAILED)
		return QSPID_ERROR;

	pQspidma->RxChNum = DmaChannel;

	/* Allocate a DMA channel */
	DmaChannel = XDMAD_AllocateChannel(pQspidma->pXdmad,
										XDMAD_TRANSFER_MEMORY, pQspidma->Qspid.qspiId);

	if (DmaChannel == XDMAD_ALLOC_FAILED)
		return QSPID_ERROR;

	pQspidma->TxChNum = DmaChannel;

	/* Setup callbacks*/
	XDMAD_SetCallback(pQspidma->pXdmad, pQspidma->RxChNum,
					  (XdmadTransferCallback)QSPID_Spi_Cb, pQspidma);

	if (XDMAD_PrepareChannel(pQspidma->pXdmad, pQspidma->RxChNum))
		return QSPID_ERROR;

	/* Setup callbacks for SPI0/1 TX (ignored) */
	XDMAD_SetCallback(pQspidma->pXdmad, pQspidma->TxChNum, NULL, NULL);

	if (XDMAD_PrepareChannel(pQspidma->pXdmad, pQspidma->TxChNum))
		return QSPID_ERROR;

	return 0;
}


/**
 * \brief Disables a QSPI Rx channel. This function will de-allocate previous
 *  allocated dma Rx channel for QSPI
 *
 * \param pQspidma  Pointer to a Spid instance.

 * \returns 0 if the transfer has been started successfully; otherwise returns
 * QSPID_ERROR_LOCK is the driver is in use, or QSPID_ERROR if the command is
 * not valid.
 */
uint32_t QSPID_DisableQspiRxChannel(QspiDma_t *pQspidma)
{

	XDMAC_SoftwareFlushReq(pQspidma->pXdmad->pXdmacs, pQspidma->RxChNum);
	XDMAD_StopTransfer(pQspidma->pXdmad, pQspidma->RxChNum);

	XDMAD_SetCallback(pQspidma->pXdmad, pQspidma->RxChNum, NULL, NULL);


	/* Free allocated DMA channel for QSPI RX. */
	XDMAD_FreeChannel(pQspidma->pXdmad, pQspidma->RxChNum);

	pQspidma->RxChNum = QSPID_CH_NOT_ENABLED;

	return 0;
}



/**
 * \brief Disables a QSPI Tx channel. This function will de-allocate previous
 * allocated dma Tx channel for QSPI
 *
 * \param pQspidma  Pointer to a Spid instance.

 * \returns 0 if the transfer has been started successfully; otherwise returns
 * QSPID_ERROR_LOCK is the driver is in use, or QSPID_ERROR if the command is
 * not valid.
 */
uint32_t QSPID_DisableQspiTxChannel(QspiDma_t *pQspidma)
{

	XDMAC_SoftwareFlushReq(pQspidma->pXdmad->pXdmacs, pQspidma->TxChNum);
	XDMAD_StopTransfer(pQspidma->pXdmad, pQspidma->TxChNum);

	XDMAD_SetCallback(pQspidma->pXdmad, pQspidma->TxChNum, NULL, NULL);

	/* Free allocated DMA channel for QSPI TX. */
	XDMAD_FreeChannel(pQspidma->pXdmad, pQspidma->TxChNum);

	pQspidma->TxChNum = QSPID_CH_NOT_ENABLED;

	return 0;
}


/**
 * \brief Disables a QSPI SPI Rx and Tx channels. This function will
 *  de-allocate privious allocated dma Rx, Txchannel for QSPI in SPI mode
 *
 * \param pQspidma  Pointer to a Spid instance.

 * \returns 0 if the transfer has been started successfully; otherwise returns
 * QSPID_ERROR_LOCK is the driver is in use, or QSPID_ERROR if the command is
 * not valid.
 */
uint32_t QSPID_DisableSpiChannel(QspiDma_t *pQspidma)
{

	XDMAC_SoftwareFlushReq(pQspidma->pXdmad->pXdmacs, pQspidma->RxChNum);
	//XDMAC_SoftwareFlushReq(pQspidma->pXdmad->pXdmacs, pQspidma->TxChNum);
	XDMAD_StopTransfer(pQspidma->pXdmad, pQspidma->RxChNum);
	XDMAD_StopTransfer(pQspidma->pXdmad, pQspidma->TxChNum);

	XDMAD_SetCallback(pQspidma->pXdmad, pQspidma->RxChNum, NULL, NULL);

	/* Free allocated DMA channel for QSPI RX. */
	XDMAD_FreeChannel(pQspidma->pXdmad, pQspidma->RxChNum);

	XDMAD_FreeChannel(pQspidma->pXdmad, pQspidma->TxChNum);

	pQspidma->RxChNum = QSPID_CH_NOT_ENABLED;
	pQspidma->TxChNum = QSPID_CH_NOT_ENABLED;

	return 0;
}


/**
 * \brief Starts a QSPI read or write operation.
 *
 * \param pQspidma  Pointer to a Qspid instance.
 * \param ReadWrite Defines the memory access type
 * \returns 0 if the transfer has been started successfully; otherwise returns
 * QSPID_ERROR_LOCK is the driver is in use, or QSPID_ERROR if the command is
 * not valid.
 */
uint32_t QSPID_ReadWriteQSPI(QspiDma_t *pQspidma, Access_t const ReadWrite)
{
	QspiBuffer_t *pBuffer = &pQspidma->Qspid.qspiBuffer;
	uint8_t chanNum;
	uint32_t semTimer = 0x7FF;

	//assert(pBuffer->pDataTx);

	if (pQspidma->progress)
		return QSPID_ERROR_LOCK;

	LockMutex(pQspidma->progress, semTimer);

	if (QSPID_configureQpsiDma
		(pQspidma, pQspidma->Qspid.pQspiFrame->Addr, pBuffer, ReadWrite))
		return QSPID_ERROR_LOCK;

	if (ReadWrite == WriteAccess) {
		chanNum =  pQspidma->TxChNum;
		SCB_CleanDCache_by_Addr((uint32_t *)pBuffer->pDataTx, pBuffer->TxDataSize);
	} else {
		if (ReadWrite != ReadAccess)
			TRACE_ERROR("%s QSPI Access Error\n\r", __FUNCTION__);
		chanNum =  pQspidma->RxChNum;
	}

	/* Start DMA 0(RX) && 1(TX) */
	if (XDMAD_StartTransfer(pQspidma->pXdmad, chanNum))
		return QSPID_ERROR_LOCK;

	return 0;
}

/**
 * \brief Starts a SPI master transfer. This is a non blocking function. It will
 *  return as soon as the transfer is started.
 *
 * \param pSpid  Pointer to a Spid instance.
 * \param pCommand Pointer to the SPI command to execute.
 * \returns 0 if the transfer has been started successfully; otherwise returns
 * SPID_ERROR_LOCK is the driver is in use, or SPID_ERROR if the command is not
 * valid.
 */
uint32_t QSPID_ReadWriteSPI(QspiDma_t *pQspidma, Access_t const ReadWrite)
{
	QspiBuffer_t *pBuffer = &pQspidma->Qspid.qspiBuffer;
	uint32_t semTimer = 0x7FF;

	assert(pBuffer->pDataRx);
	assert(pBuffer->pDataTx);

	/* Try to get the dataflash semaphore */
	if (pQspidma->progress)

		return QSPID_ERROR_LOCK;

	LockMutex(pQspidma->progress, semTimer);

	if (QSPID_configureQpsiDma
		(pQspidma, pQspidma->Qspid.pQspiFrame->Addr, pBuffer, ReadWrite))
		return QSPID_ERROR_LOCK;

	SCB_CleanDCache_by_Addr((uint32_t *)pBuffer->pDataTx, pBuffer->TxDataSize);

	/* Start DMA 0(RX) && 1(TX) */
	if (XDMAD_StartTransfer(pQspidma->pXdmad, pQspidma->RxChNum))
		return QSPID_ERROR_LOCK;

	if (XDMAD_StartTransfer(pQspidma->pXdmad, pQspidma->TxChNum))
		return QSPID_ERROR_LOCK;

	return 0;
}

/**
 * \brief Check if the QSPI driver is busy.
 *
 * \param pSpid  Pointer to a Spid instance.
 * \returns 1 if the SPI driver is currently busy executing a command; otherwise
 */
uint32_t QSPID_IsBusy(volatile uint8_t *QspiSemaphore)
{
	if (Is_LockFree(QspiSemaphore))
		return 1;
	else
		return 0;
}
